use specs::world::Index;
use specs::{Component, Entities, WriteStorage, System};
use std::sync::mpsc::Receiver;

use crate::validators::validator::Validator;

pub struct SetMessage<T: Component> {
    entity_id: Index,
    value: T,
    who: Index,
}

impl<T: Component> SetMessage<T> {
    pub fn new(entity_id: Index, value: T, who: Index) -> Self {
        SetMessage{
            entity_id,
            value,
            who,
        }
    }
}

pub struct SetSystem<T: Component, V> {
    rc: Receiver<SetMessage<T>>,
    validator: V,
}

impl<T: Component, V> SetSystem<T, V> {
    pub fn new(rc: Receiver<SetMessage<T>>, validator: V) -> Self {
        SetSystem {
            rc,
            validator
        }
    }
}

impl<'a, T: Component, V: Validator<'a>> System<'a> for SetSystem<T, V> {
    type SystemData = (
        Entities<'a>,
        WriteStorage<'a, T>,
        V::SystemData,
    );

    fn run(&mut self, data: Self::SystemData) {
        let (entities, mut value_storage, validator_data) = data;

        let received = self.rc.try_recv();

        if let Ok(set) = received {
            let entity = entities.entity(set.entity_id);
            let who_entity = entities.entity(set.who);

            if self.validator.validate(&validator_data, who_entity, entity) {
                if let Some(value) = value_storage.get_mut(entity) {
                    *value = set.value;
                }
            }
        }
    }
}
